--- added by wsh @ 2017-12-09
---@class LuaAction @回调方法封装,可变参数处于最后一位

--- 自定义类型
ClassType = {
    class = 1,
    instance = 2,
}

---关键字
Keywords = {
    call = "__call",
    class = "__class",
    delete = "__delete",
    index = "__index",

    newindex = "__newindex",
    classType = "__classType",
    className = "__className",
    luaActions = "__luaActions",
    luaObject = "LuaObject",

    super = "super",
    --构造函数
    ctor = "__ctor",
    --析构函数
    dtor = "__dtor",
    ---格式化
    tostring = "__tostring",
}

local actionMeta = function(tb, ...)
    local status, err
    local obj = tb[1]
    local func = tb[2]
    local param = tb[3]

    if param ~= nil then
        local len = select("#", ...)
        if len == 0 then
            status, err = pcall(func, obj, param)
        else
            status, err = pcall(func, obj, param, ...)
        end
    else
        status, err = pcall(func, obj, ...)
    end

    if not status then
        error(err .. "\n" .. debug.traceback())
    end
    return status
end

---获取一个LuaAction
---@param self LuaObject
---@param action function @方法
---@param param any @其他参数<在最后一位>
---@return LuaAction
local getLuaAction = function(self, action, param)
    if param == nil then
        local luaAction = self.__luaActions[action]

        if luaAction == nil then
            luaAction = CreateLuaAction(action, self, param)
            self.__luaActions[action] = luaAction
        end
        return luaAction
    else
        return CreateLuaAction(action, self, param)
    end
end

---所有lua类的基类
---@class LuaObject
---@field public GetLuaAction fun(self:LuaObject, action:LuaAction, param:any) @方法调用
---@field public ClassName fun(self:LuaObject) @方法调用
LuaObject = {
    super = nil,
    ---类名
    __className = Keywords.luaObject,
    __classType = ClassType.class,

    __index = false,
    __newindex = false,

    ---构造函数
    __ctor = function(self)
        self.__luaActions = {}
    end,
    
    ---析构函数
    __dtor = function(self)
        self.__luaActions = nil
    end,

    ---获取到当前类型
    ---@param self LuaObject
    GetType = function(self)
        return self.__class
    end,

    ---获取到当前类型名称
    ClassName = function(self)
        return self.__className
    end,

    ---设置原方法
    GetMetaFunc = function()
        return {
            __index = function(tb, key)
                local cls = rawget(tb, "__class")
                return cls[key]
            end,

            __tostring = function(tb)
                return tb.__className
            end
        }
    end,
    GetLuaAction = getLuaAction
}

---保存类类型的虚表
local _classList = {}
_classList[Keywords.luaObject] = LuaObject
local nilFunc = function() end

local getIndex = function(t, k)
    local cls = t
    local val = nil

    while cls ~= nil do
        val = rawget(cls, k)
        if val then
            return val
        end
        cls = rawget(cls, Keywords.super)
    end
    return val
end

---创建一个新类
---@param class LuaObject
---@return LuaObject
local function newClass(class, ...)
    --- 生成一个类对象
    local obj = {
        __class = class,
        __classType = ClassType.instance,
        __className = class.__className
    }

    --- 保存类的虚表
    local meta = {__mode = "k"}
    local metaFuncs = class:GetMetaFunc()
    for key, value in pairs(metaFuncs) do
        meta[key] = value
    end
    setmetatable(obj, meta)

    local create
    create = function(c, ...)
        if c.super then
            create(c.super, ...)
        end

        if c.__ctor then
            c.__ctor(obj, ...)
        end
    end
    create(obj.super, ...)

    if obj.__ctor then
        local len = select("#", ...)
        if len == 0 then
            obj.__ctor(obj)
        else
            obj.__ctor(obj, ...)
        end
    end
    return obj
end

---@param classname string
---@param super? LuaObject
---@return LuaObject
function BaseClass(classname, super)
    assert(type(classname) == "string" and #classname > 0)
    assert(_classList[classname] == nil, '"' .. tostring(classname) .. '" 重复定义!!')

    --- 生成一个类模型
    local clsSource = {}
    clsSource.super = super or LuaObject
    setmetatable(clsSource,{__call = newClass, __index = getIndex})
    clsSource.__className = classname
    clsSource.__classType = ClassType.class

    clsSource.__ctor = nilFunc
    clsSource.__dtor = nilFunc
    _classList[classname] = clsSource
    return clsSource
end

---@param action function 方法
---@param tb LuaObject
---@param param any 参数
---@return LuaAction
function CreateLuaAction(action, tb, param)
    local luaAction = {tb, action, param}
    return setmetatable(luaAction, {__call = actionMeta})
end

---检测lua类实例是否是某个类型
---@param instance LuaObject lua类实例
---@param ty LuaObject|string 类型|类型名
---@return boolean
function IsType(instance, ty)
    local luaType = type(instance)
    if luaType ~= "table" then
        return false
    end
    
    if instance.__classType ~= ClassType.instance then
        print("不是一个lua类实体")
        return false
    end
    return IsSubType(instance, ty)
end

---检测lua类的父子关系
---@param childType LuaObject 子类型
---@param parentType LuaObject 父类型
---@return boolean
function IsSubType(childType, parentType)
    if not parentType then
        return false
    end

    local cls = childType
    while cls do
        if cls.ClassName == parentType.ClassName then
            return true
        end
        cls = cls.super
    end
    return false
end